package com.dreamlin.lindownload.thread;

import android.annotation.SuppressLint;

import androidx.annotation.NonNull;

import com.dreamlin.lindownload.LinDownload;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import io.realm.Realm;

/**
 * <p> Title: LinRunnable </p>
 * <p> Description: </p>
 *
 * @author: dreamlin
 * @date: 2020/5/5
 * @version: V1.0.0
 * Created by dreamlin on 2020/5/5.
 */
public class LinRunnable implements Runnable {

    private Realm realm;
    private CountDownLatch countDownLatch;
    private LinThreadInfoImp linThreadInfoImp;
    private int status = LinDownload.STATUS_NONE;
    private long finished;
    private ScheduledExecutorService scheduledExecutor;

    @SuppressLint("DefaultLocale")
    public LinRunnable(@NonNull CountDownLatch countDownLatch, @NonNull LinThreadInfoImp linThreadInfoImp, int index) {
        this.countDownLatch = countDownLatch;
        this.linThreadInfoImp = linThreadInfoImp;
        scheduledExecutor = Executors.newScheduledThreadPool(1);
        linThreadInfoImp.setId(String.format("%s-%d", linThreadInfoImp.getFileName(), index));
    }

    public void pause() {
        status = LinDownload.STATUS_PAUSE;
        //TODO:更新数据库
        scheduledExecutor.shutdown();
        linThreadInfoImp.update(realm);
    }

    public void cancel() {
        status = LinDownload.STATUS_CANCEL;
        scheduledExecutor.shutdown();
        //TODO:删除数据库及文件信息
        linThreadInfoImp.delete(realm);
    }

    public long getFinished() {
        return linThreadInfoImp == null ? -1L : linThreadInfoImp.getFinished();
    }

    public boolean isFinish() {
        return linThreadInfoImp != null && linThreadInfoImp.getFinished() >= (linThreadInfoImp.getEnd() - linThreadInfoImp.getStart() + 1);
    }

    @SuppressLint("DefaultLocale")
    @Override
    public void run() {
        if (linThreadInfoImp == null) {
            return;
        }
        if (realm == null || realm.isClosed()) {
            realm = Realm.getDefaultInstance();
        }
        if (isFinish()) {
            countDownLatch.countDown();
            scheduledExecutor.shutdown();
            linThreadInfoImp.delete(realm);
            return;
        }
        if (scheduledExecutor == null || scheduledExecutor.isShutdown()) {
            scheduledExecutor = Executors.newScheduledThreadPool(1);
        }
        scheduledExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (!scheduledExecutor.isShutdown())
                    linThreadInfoImp.update(realm);
            }
        }, linThreadInfoImp.getCallInterval(), linThreadInfoImp.getCallInterval(), TimeUnit.MILLISECONDS);
        long seekStart = linThreadInfoImp.getStart() + linThreadInfoImp.getFinished();
        HttpURLConnection conn = null;
        RandomAccessFile accessFile = null;
        InputStream inputStream = null;
        try {
            URL url = new URL(linThreadInfoImp.getUrl());
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Range",
                    String.format("bytes=%d-%d", seekStart, linThreadInfoImp.getEnd()));
            conn.connect();
            status = LinDownload.STATUS_DOWNLOAD;
            if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
                File file = new File(linThreadInfoImp.getSavePath(), linThreadInfoImp.getFileName());
                if (!file.exists()) {
                    throw new FileNotFoundException();
                }
                accessFile = new RandomAccessFile(file, "rwd");

                accessFile.seek(seekStart);
                inputStream = conn.getInputStream();
                byte[] buffer = new byte[1024 << 2];
                int len;
                finished = linThreadInfoImp.getFinished();
                while ((len = inputStream.read(buffer)) != -1) {
                    if (status == LinDownload.STATUS_PAUSE) {
                        //更新数据库
                        linThreadInfoImp.update(realm);
                        return;
                    } else if (status == LinDownload.STATUS_CANCEL) {
                        //TODO:删除数据库信息
                        linThreadInfoImp.delete(realm);
                        return;
                    } else if (status == LinDownload.STATUS_DOWNLOAD) {
                        accessFile.write(buffer, 0, len);
                        finished += len;
                        linThreadInfoImp.setFinished(finished);
                        //TODO:更新数据库,这里更新和回调时间同步吧
                    }
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (accessFile != null)
                    accessFile.close();
                if (inputStream != null)
                    inputStream.close();
                if (conn != null)
                    conn.disconnect();
                scheduledExecutor.shutdown();
                //TODO:完成后数据库删除线程信息
                if (isFinish())
                    linThreadInfoImp.delete(realm);
                realm.close();
                realm = null;
                countDownLatch.countDown();
                countDownLatch = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
